import aiohttp
import asyncio
import base64
import os
import json
import urllib.parse  # 新增这行，用于解析URL路径
import time
from typing import List, Optional
import logging
from config.settings import VOLCANO_API_KEY, VOLCANO_API_URL, VOLCANO_MODEL_NAME

logger = logging.getLogger(__name__)


class VolcanoClient:
    def __init__(self):
        self.api_key = VOLCANO_API_KEY
        self.api_url = VOLCANO_API_URL
        self.model_name = VOLCANO_MODEL_NAME
        self.supported_image_formats = {
            '.jpg': 'jpeg', '.jpeg': 'jpeg', '.png': 'png', '.gif': 'gif',
            '.webp': 'webp', '.bmp': 'bmp', '.tiff': 'tiff', '.ico': 'ico',
            '.heic': 'heic', '.heif': 'heif'
        }
        self.client = aiohttp.ClientSession()

    async def close(self):
        """关闭HTTP会话"""
        await self.client.close()

    async def encode_image_to_base64(self, image_path):
        """将图片（本地路径或网络URL）编码为base64格式"""
        try:
            # 区分网络URL和本地路径
            if image_path.startswith(('http://', 'https://')):
                # 网络图片：先下载
                image_data_bin = await self.download_image_from_url(image_path)
                if not image_data_bin:
                    return None, None
                # 优化：从URL提取扩展名（处理没有扩展名的情况）
                path = urllib.parse.urlparse(image_path).path
                ext = os.path.splitext(path.lower())[1] or '.jpg'  # 无扩展名时默认.jpg
            else:
                # 本地图片：直接读取
                ext = os.path.splitext(image_path.lower())[1]
                with open(image_path, "rb") as image_file:
                    image_data_bin = image_file.read()

            if ext not in self.supported_image_formats:
                logger.warning(f"不支持的图片格式: {ext}，路径/URL: {image_path}")
                return None, None

            img_format = self.supported_image_formats[ext]
            image_data = base64.b64encode(image_data_bin).decode('utf-8')
            logger.info(f"图片编码成功: {image_path}，格式: {img_format}")
            return img_format, image_data
        except Exception as e:
            logger.error(f"图片编码失败，路径/URL: {image_path}，错误: {str(e)}")
            return None, None

    async def download_image_from_url(self, url):
        """从网络URL下载图片，返回二进制数据"""
        try:
            # 处理URL中的特殊字符（如 | 等，避免下载失败）
            encoded_url = urllib.parse.quote(url, safe=':/?&=')
            async with self.client.get(encoded_url, timeout=15) as response:
                if response.status != 200:
                    logger.warning(f"下载图片失败，URL: {url}，状态码: {response.status}")
                    return None
                return await response.read()  # 返回二进制图片数据
        except Exception as e:
            logger.error(f"下载网络图片出错，URL: {url}，错误: {str(e)}")
            return None

    async def call_volcano_api(
            self,
            system_prompt: str,
            user_prompt: str,
            image_paths: Optional[List[str]] = None,
            max_retries: int = 3
    ) -> str:
        """调用火山API"""
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}"
        }

        # 构建消息体
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": [{"type": "text", "text": user_prompt}]}
        ]

        # 添加图片
        if image_paths:
            for img_path in image_paths[:3]:  # 最多3张图
                # 关键：异步调用编码方法，需要加await
                img_format, img_base64 = await self.encode_image_to_base64(img_path)
                if img_format and img_base64:
                    messages[1]["content"].append({
                        "type": "image_url",
                        "image_url": {"url": f"data:image/{img_format};base64,{img_base64}"}
                    })

        payload = {
            "model": self.model_name,
            "messages": messages,
            "temperature": 0.1,
            "max_tokens": 2048,
            "stream": False,
            "thinking": {"type": "disabled"}
        }

        # 带重试的请求
        for attempt in range(max_retries):
            try:
                async with self.client.post(
                        self.api_url,
                        headers=headers,
                        json=payload,
                        timeout=60
                ) as response:
                    response_text = await response.text()
                    logger.info(f"火山API响应: {response_text[:500]}...")

                    if response.status in [400, 401, 404]:
                        return f"API错误 {response.status}: {response_text}"

                    response.raise_for_status()
                    result = await response.json()
                    if "choices" in result and result["choices"]:
                        return result["choices"][0]["message"]["content"]
                    return f"空结果: {result}"

            except Exception as e:
                logger.error(f"API调用失败 (尝试 {attempt + 1}/{max_retries}): {str(e)}")
                if attempt < max_retries - 1:
                    await asyncio.sleep(5 * (attempt + 1))

        return "超过最大重试次数"


async def get_volcano_client():
    """依赖注入：获取火山客户端实例"""
    client = VolcanoClient()
    try:
        yield client
    finally:
        await client.close()


# 修改encode_image_to_base64方法，支持网络URL（替换原方法）
# async def encode_image_to_base64(self, image_path):
#     """将图片（本地路径或网络URL）编码为base64格式"""
#     try:
#         # 区分网络URL和本地路径
#         if image_path.startswith(('http://', 'https://')):
#             # 网络图片：先下载
#             image_data_bin = await self.download_image_from_url(image_path)
#             if not image_data_bin:
#                 return None, None
#             # 从URL提取文件格式
#             ext = os.path.splitext(image_path.lower())[1]
#         else:
#             # 本地图片：直接读取
#             ext = os.path.splitext(image_path.lower())[1]
#             with open(image_path, "rb") as image_file:
#                 image_data_bin = image_file.read()
#
#         if ext not in self.supported_image_formats:
#             logger.warning(f"不支持的图片格式: {ext}，路径/URL: {image_path}")
#             return None, None
#
#         img_format = self.supported_image_formats[ext]
#         image_data = base64.b64encode(image_data_bin).decode('utf-8')
#         logger.info(f"图片编码成功: {image_path}，格式: {img_format}")
#         return img_format, image_data
#     except Exception as e:
#         logger.error(f"图片编码失败，路径/URL: {image_path}，错误: {str(e)}")
#         return None, None
